package turtle.system.invest.evaluate;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

import org.osite.util.Combination;
import org.osite.util.FileResourceLoader;
import org.osite.util.Preconditions;
import org.osite.util.ResourceLoader;
import org.osite.util.Series;
import org.osite.util.ToolKit;
import org.osite.xml.DefaultEntityHandler;
import org.osite.xml.ParseContext;
import org.osite.xml.ParseSetupException;
import org.osite.xml.XmlEntity;
import org.osite.xml.XmlMember;
import org.osite.xml.XmlParseException;
import org.osite.xml.XmlParser;
import org.osite.xml.XmlUtil;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import turtle.system.invest.Environment;
import turtle.system.invest.rule.InvestRunnable;
import turtle.system.invest.rule.Rule;
import turtle.system.invest.rule.WinnieRule;

@XmlEntity(value = "FindBestParams", handler =  FindBestParams.Handler.class)
public class FindBestParams {
    private final static String SIMULATE_TAG = "Simulate";

    private final Environment environment = new Environment();
    
    private final ArrayList<InvestRunnable> setupTasks = ToolKit.newArrayList();
    private final ArrayList<InvestRunnable> postTasks = ToolKit.newArrayList();
 
    private Document simulateDoc;
    private Simulate simulate;
    private Combination combination;
    private XmlParser parser;

    @XmlMember
    public void addSetupTasks(InvestRunnable[] tasks) {
        setupTasks.addAll(Arrays.asList(tasks));
    }

    @XmlMember
    public void addPostTasks(InvestRunnable[] tasks) {
        postTasks.addAll(Arrays.asList(tasks));
    }

    @XmlMember(identifyByType = true)
    public void setCombination(Combination combination) {
    	this.combination = combination;
    }

    private void runTasks(ArrayList<InvestRunnable> tasks) {
        for (InvestRunnable task : tasks) {
            task.run(environment);
        }
        tasks.clear();
    }

    public void run(ResourceLoader loader) {
        environment.setResourceLoader(loader);
        runTasks(setupTasks);
        while (combination.next()) {
        	try {
				Simulate simulate = (Simulate) parser.parse(
						getTranlatedDocument(simulateDoc).getDocumentElement());
				simulate.setEnvironment(environment);
				simulate.run(loader);
			} catch (XmlParseException e) {
				throw new RuntimeException(e);
			}
        }
        runTasks(postTasks);
    }

    public static String makeKey(String name) {
        return "org.osite.invest" + name;
    }
        
    private static class Handler extends DefaultEntityHandler<FindBestParams> {

		public Handler(XmlParser parser) throws ParseSetupException {
			super(FindBestParams.class, parser);
		}
    	
	    public void processMember(FindBestParams entity, Element member,
	            ParseContext context) throws XmlParseException {
	    	if (member.getTagName().equals(SIMULATE_TAG)) {
	    		Document doc = XmlUtil.newDocument();
	    		doc.appendChild(doc.importNode(member, true));
	    		entity.simulateDoc = doc;
	    		XmlUtil.printNode(entity.simulateDoc);
	    	} else {
	    		super.processMember(entity, member, context);
	    	}
	    }
    }
    
    private Document getTranlatedDocument(Document document) {
    	final int VARIABLE_OFFSET = 1;
    	Document doc = (Document) document.cloneNode(true);
    	LinkedList<Element> stack = ToolKit.newLinkedList();
    	stack.add(doc.getDocumentElement());
    	while (!stack.isEmpty()) {
    		Element element = stack.removeFirst();
    		NamedNodeMap map = element.getAttributes();
    		for (int i = 0, n = map.getLength(); i < n; ++i) {
    			Attr attr = (Attr) map.item(i);
    			String value = attr.getValue();
    			if (value.startsWith("@")) {
    				attr.setValue((String) combination.get(
    						value.substring(VARIABLE_OFFSET)));
    			}
    		}
    		NodeList list = element.getChildNodes();
    		for (int i = 0, n = list.getLength(); i < n; ++i) {
    			Node node = list.item(i);
    			if (node.getNodeType() == Node.ELEMENT_NODE) {
    				stack.addLast((Element) node);
    			}
    		}
    	}
    	return doc;
    }

	private void setParser(XmlParser parser) {
		this.parser = Preconditions.checkNotNull(parser);
	}

    public static void main(String argv[]) throws Exception {
    	long time = System.currentTimeMillis();
        XmlParser parser = new XmlParser()
                .addDefaultAttributeConvertors()
                .registerComponent(OpenFile.class)
                .registerComponent(CloseFile.class)
                .registerComponent(Simulate.class)
                .registerComponent(FindBestParams.class)
                .registerComponent(PrintProfit.class)
                .registerComponent(IndexBuilder.class)
                .registerComponent(TimestampSetter.class)
                .registerComponent(WinnieRule.class)
                .registerComponent(SetVariable.class)
                .registerComponent(Combination.class)
                .registerComponent(Series.class);
        ResourceLoader resourceLoader = new FileResourceLoader(new File("."));
        Document doc = XmlUtil.parseDocument(
                resourceLoader.getResource("bestParams.xml"));
        FindBestParams findParams = 
        		(FindBestParams) parser.parse(doc.getDocumentElement());
        findParams.setParser(parser);
        findParams.run(resourceLoader);
        
        System.out.println(System.currentTimeMillis() - time);
    }
}
